Completed
Push — master ( e7e1b8...a26a04 )
by Emil
02:49
created

auth.checkAPIKey   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6.2163

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 12
nc 7
nop 3
dl 0
loc 23
ccs 9
cts 11
cp 0.8182
crap 6.2163
rs 8.6666
c 1
b 0
f 0
1 1
const db = require("../db/database.js");
2 1
const hat = require("hat");
3 1
const validator = require("email-validator");
4
5 1
const bcrypt = require('bcrypt');
6 1
const jwt = require('jsonwebtoken');
7
8
let config;
9
10 1
try {
11 1
    config = require('../../config/config.json');
12
} catch (error) {
13 1
    console.error(error);
14
}
15
16 2
const jwtSecret = process.env.JWT_SECRET || config.secret;
17
18 1
const auth = {
19
    checkAPIKey: function (req, res, next) {
20 108
        if ( req.path == '/') {
21 1
            return next();
22
        }
23
24 107
        if ( req.path == '/v2') {
25
            return next();
26
        }
27
28 107
        if ( req.path == '/auth/api_key') {
29 1
            return next();
30
        }
31
32 106
        if ( req.path == '/auth/api_key/confirmation') {
33 11
            return next();
34
        }
35
36 95
        if ( req.path == '/auth/api_key/deregister') {
37
            return next();
38
        }
39
40 95
        auth.isValidAPIKey(req.query.api_key || req.body.api_key, next, req.path, res);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
41
    },
42
43
    isValidAPIKey: function(apiKey, next, path, res) {
44 95 View Code Duplication
        db.get("SELECT email FROM apikeys WHERE key = ?", apiKey, (err, row) => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
45 95
            if (err) {
46
                return res.status(500).json({
47
                    errors: {
48
                        status: 500,
49
                        source: path,
50
                        title: "Database error",
51
                        detail: err.message
52
                    }
53
                });
54
            }
55
56 95
            if (row !== undefined) {
57 86
                return next();
58
            }
59
60 9
            return res.status(401).json({
61
                errors: {
62
                    status: 401,
63
                    source: path,
64
                    title: "Valid API key",
65
                    detail: "No valid API key provided."
66
                }
67
            });
68
        });
69
    },
70
71
    getNewAPIKey: function(res, email) {
72 9
        let data = {
73
            apiKey: ""
74
        };
75
76 9
        if (email === undefined || !validator.validate(email)) {
77 1
            data.message = "A valid email address is required to obtain an API key.";
78 1
            data.email = email;
79
80 1
            return res.render("api_key/form", data);
81
        }
82
83 8
        db.get("SELECT email, key FROM apikeys WHERE email = ?", email, (err, row) => {
84 8
            if (err) {
85
                data.message = "Database error: " + err.message;
86
                data.email = email;
87
88
                return res.render("api_key/form", data);
89
            }
90
91 8
            if (row !== undefined) {
92
                data.apiKey = row.key;
93
94
                return res.render("api_key/confirmation", data);
95
            }
96
97 8
            return auth.getUniqueAPIKey(res, email);
98
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
99
    },
100
101
    getUniqueAPIKey: function(res, email) {
102 8
        const apiKey = hat();
103 8
        let data = {
104
            apiKey: ""
105
        };
106
107 8
        db.get("SELECT key FROM apikeys WHERE key = ?", apiKey, (err, row) => {
108 8
            if (err) {
109
                data.message = "Database error: " + err.message;
110
                data.email = email;
111
112
                return res.render("api_key/form", data);
113
            }
114
115 8
            if (row === undefined) {
116 8
                db.run("INSERT INTO apikeys (key, email) VALUES (?, ?)",
117
                    apiKey,
118
                    email, (err) => {
119 8
                        if (err) {
120
                            data.message = "Database error: " + err.message;
121
                            data.email = email;
122
123
                            return res.render("api_key/form", data);
124
                        }
125
126 8
                        data.apiKey = apiKey;
127
128 8
                        return res.render("api_key/confirmation", data);
129
                    });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
130
            } else {
131
                return auth.getUniqueAPIKey(res, email);
132
            }
133
        });
134
    },
135
136
    deregister: function(res, body) {
137
        const email = body.email;
138
        const apiKey = body.apikey;
139
140
        db.get("SELECT key FROM apikeys WHERE key = ? and email = ?",
141
            apiKey,
142
            email,
143
            (err, row) => {
144 2
                if (err) {
145
                    let data = {
146
                        message: "Database error: " + err.message,
147
                        email: email,
148
                        apikey: apiKey
149
                    };
150
151
                    return res.render("api_key/deregister", data);
152
                }
153
154 2
                if (row === undefined) {
155
                    let data = {
156
                        message: "The E-mail and API-key combination does not exist.",
157
                        email: email,
158
                        apikey: apiKey
159
                    };
160
161
                    return res.render("api_key/deregister", data);
162
                }
163
164
                return auth.deleteData(res, apiKey, email);
165
            });
166
    },
167
168
    deleteData: function(res, apiKey, email) {
169
        let errorMessages = [];
170
171
        db.run("DELETE FROM apikeys WHERE key = ?",
172
            apiKey,
173
            (err) => {
174 2
                if (err) {
175
                    errorMessages.push(err);
176
                }
177
178
                db.run("DELETE FROM deliveries WHERE apiKey = ?",
179
                    apiKey,
180
                    (err) => {
181 2
                        if (err) {
182
                            errorMessages.push(err);
183
                        }
184
185
                        db.run("DELETE FROM invoices WHERE apiKey = ?",
186
                            apiKey,
187
                            (err) => {
188 2
                                if (err) {
189
                                    errorMessages.push(err);
190
                                }
191
192
                                db.run("DELETE FROM orders WHERE apiKey = ?",
193
                                    apiKey,
194
                                    (err) => {
195 2
                                        if (err) {
196
                                            errorMessages.push(err);
197
                                        }
198
199
                                        db.run("DELETE FROM order_items WHERE apiKey = ?",
200
                                            apiKey,
201
                                            (err) => {
202 2
                                                if (err) {
203
                                                    errorMessages.push(err);
204
                                                }
205
206
                                                db.run("DELETE FROM products WHERE apiKey = ?",
207
                                                    apiKey,
208
                                                    (err) => {
209 2
                                                        if (err) {
210
                                                            errorMessages.push(err);
211
                                                        }
212
213
                                                        db.run("DELETE FROM users WHERE apiKey = ?",
214
                                                            apiKey,
215
                                                            (err) => {
216 2
                                                                if (err) {
217
                                                                    errorMessages.push(err);
218
                                                                }
219
220
                                                                return auth.afterDelete(
221
                                                                    res,
222
                                                                    apiKey,
223
                                                                    email,
224
                                                                    errorMessages
225
                                                                );
226
                                                            });
227
                                                    });
228
                                            });
229
                                    });
230
                            });
231
                    });
232
            });
233
    },
234
235
    afterDelete: function(res, apiKey, email, errorMessages) {
236 2
        if (errorMessages.length > 0) {
237
            let data = {
238
                message: "Could not delete data due to: " +
239
                    errorMessages.join(" | "),
240
                email: email,
241
                apikey: apiKey
242
            };
243
244
            return res.render("api_key/deregister", data);
245
        }
246
247
        let data = {
248
            message: "All data has been deleted",
249
            email: ""
250
        };
251
252
        return res.render("api_key/form", data);
253
    },
254
255
    login: function(res, body) {
256 6
        const email = body.email;
257 6
        const password = body.password;
258 6
        const apiKey = body.api_key;
259
260 6
        if (!email || !password) {
261 2
            return res.status(401).json({
262
                errors: {
263
                    status: 401,
264
                    source: "/login",
265
                    title: "Email or password missing",
266
                    detail: "Email or password missing in request"
267
                }
268
            });
269
        }
270
271 4
        db.get("SELECT * FROM users WHERE apiKey = ? AND email = ?",
272
            apiKey,
273
            email,
274 View Code Duplication
            (err, rows) => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
275 4
                if (err) {
276
                    return res.status(500).json({
277
                        errors: {
278
                            status: 500,
279
                            source: "/login",
280
                            title: "Database error",
281
                            detail: err.message
282
                        }
283
                    });
284
                }
285
286 4
                if (rows === undefined) {
287 1
                    return res.status(401).json({
288
                        errors: {
289
                            status: 401,
290
                            source: "/login",
291
                            title: "User not found",
292
                            detail: "User with provided email not found."
293
                        }
294
                    });
295
                }
296
297 3
                const user = rows;
298
299 3
                bcrypt.compare(password, user.password, (err, result) => {
300 3
                    if (err) {
301
                        return res.status(500).json({
302
                            errors: {
303
                                status: 500,
304
                                source: "/login",
305
                                title: "bcrypt error",
306
                                detail: "bcrypt error"
307
                            }
308
                        });
309
                    }
310
311 3
                    if (result) {
312 2
                        let payload = { api_key: user.apiKey, email: user.email };
313 2
                        let jwtToken = jwt.sign(payload, jwtSecret, { expiresIn: '24h' });
314
315 2
                        return res.json({
316
                            data: {
317
                                type: "success",
318
                                message: "User logged in",
319
                                user: payload,
320
                                token: jwtToken
321
                            }
322
                        });
323
                    }
324
325 1
                    return res.status(401).json({
326
                        errors: {
327
                            status: 401,
328
                            source: "/login",
329
                            title: "Wrong password",
330
                            detail: "Password is incorrect."
331
                        }
332
                    });
333
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
334
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
335
    },
336
337
    register: function(res, body) {
338 5
        const email = body.email;
339 5
        const password = body.password;
340 5
        const apiKey = body.api_key;
341
342 5
        if (!email || !password) {
343 2
            return res.status(401).json({
344
                errors: {
345
                    status: 401,
346
                    source: "/register",
347
                    title: "Email or password missing",
348
                    detail: "Email or password missing in request"
349
                }
350
            });
351
        }
352
353 3 View Code Duplication
        bcrypt.hash(password, 10, function(err, hash) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
354 3
            if (err) {
355
                return res.status(500).json({
356
                    errors: {
357
                        status: 500,
358
                        source: "/register",
359
                        title: "bcrypt error",
360
                        detail: "bcrypt error"
361
                    }
362
                });
363
            }
364
365 3
            db.run("INSERT INTO users (apiKey, email, password) VALUES (?, ?, ?)",
366
                apiKey,
367
                email,
368
                hash, (err) => {
369 3
                    if (err) {
370 1
                        return res.status(500).json({
371
                            errors: {
372
                                status: 500,
373
                                source: "/register",
374
                                title: "Database error",
375
                                detail: err.message
376
                            }
377
                        });
378
                    }
379
380 2
                    return res.status(201).json({
381
                        data: {
382
                            message: "User successfully registered."
383
                        }
384
                    });
385
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
386
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
387
    },
388
389
    checkToken: function(req, res, next) {
390 14
        var token = req.headers['x-access-token'];
391
392 14 View Code Duplication
        if (token) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
393 11
            jwt.verify(token, jwtSecret, function(err, decoded) {
394 11
                if (err) {
395
                    return res.status(500).json({
396
                        errors: {
397
                            status: 500,
398
                            source: req.path,
399
                            title: "Failed authentication",
400
                            detail: err.message
401
                        }
402
                    });
403
                }
404
405 11
                req.user = {};
406 11
                req.user.api_key = decoded.api_key;
407 11
                req.user.email = decoded.email;
408
409 11
                next();
410
411 11
                return undefined;
412
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
413
        } else {
414 3
            return res.status(401).json({
415
                errors: {
416
                    status: 401,
417
                    source: req.path,
418
                    title: "No token",
419
                    detail: "No token provided in request headers"
420
                }
421
            });
422
        }
423
    }
424
};
425
426
module.exports = auth;
427